Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

[AGENT-122] Query for logs with tail option#342

Merged
pec1985 merged 3 commits intomainfrom
AGENT-122
May 27, 2025
Merged

[AGENT-122] Query for logs with tail option#342
pec1985 merged 3 commits intomainfrom
AGENT-122

Conversation

@pec1985
Copy link
Copy Markdown
Contributor

@pec1985 pec1985 commented May 23, 2025

This is a first pass, mergable, but I'm sure there's a lot of room for improvement.

Neat arguments you can pass: --hideDate, --hideTime, --tail, --since

Summary by CodeRabbit

  • New Features
    • Introduced a new CLI command to view and filter logs by various criteria such as agent, deployment, environment, project, organization, session, and severity.
    • Added flexible time filtering with support for custom duration formats, including days.
    • Enabled real-time log streaming with a "tail" mode.
    • Provided customizable log output formatting, including options to hide date or time.
    • Improved log readability with styled severity labels and timestamps.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2025

Walkthrough

A new CLI command logs has been added to fetch, filter, and display logs from a remote API. It supports streaming (tail), filtering by multiple criteria, flexible duration parsing, and configurable output formatting with styled severity and timestamps.

Changes

File(s) Change Summary
cmd/logs.go Implemented the logs command with filtering, streaming, flexible duration parsing, output formatting (styled text), and API integration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI (logsCmd)
    participant API Client
    participant Remote API

    User->>CLI (logsCmd): Run `logs` command with flags
    CLI (logsCmd)->>API Client: Build query from flags
    API Client->>Remote API: Request logs with filters
    Remote API-->>API Client: Return logs data
    API Client->>CLI (logsCmd): Provide logs
    CLI (logsCmd)->>User: Display formatted logs
    alt Tail mode enabled
        loop Every 5 seconds
            CLI (logsCmd)->>API Client: Request new logs since last timestamp
            API Client->>Remote API: Fetch logs
            Remote API-->>API Client: Return new logs
            API Client->>CLI (logsCmd): Provide new logs
            CLI (logsCmd)->>User: Display new logs
        end
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Support for fetching and streaming project logs (AGENT-122) The command implements fetching and tail streaming.
Filtering logs by various fields (AGENT-122) Multiple filter flags are supported, including severity, environment, project, etc.
Output formats (text and JSON), color coding, and tail support (AGENT-122) JSON output formatting is not explicitly implemented; color coding is present in text output.

Poem

Logs now flow with colors bright,
Filters, tails, and time in sight.
Agents, projects, all revealed—
Secrets from the past unsealed.
Rabbits cheer with every line,
For CLI logs now work just fine!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57ac106 and ca1b80d.

📒 Files selected for processing (1)
  • cmd/logs.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/logs.go
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Test CLI Upgrade Path (windows-latest)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment thread cmd/logs.go
dateFormat = ""
}

prevIds := []string{}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This array is to filter already shown logs from previous fetch. Issue is that passing in the last timestamp as startDate return results of that time (or later). I could not find a way around this

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
cmd/logs.go (3)

20-41: Remove redundant case conversion.

The regex already uses the case-insensitive flag (?i), making the strings.ToLower call redundant.

Apply this diff to simplify the code:

-	switch strings.ToLower(matches[2]) {
+	switch matches[2] {
-	case "m":
+	case "m", "M":
-	case "h":
+	case "h", "H":
-	case "d":
+	case "d", "D":

Alternatively, remove the case-insensitive flag and keep the ToLower call for consistency.


124-139: Consider extracting the polling logic into a separate function.

The anonymous function with the infinite loop makes the code harder to read and test. Consider extracting it into a named function.

+func pollLogs(ctx context.Context, fetcher func() bool, tail bool) {
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		default:
+			if success := fetcher(); !success {
+				return
+			}
+			if !tail {
+				return
+			}
+			time.Sleep(5 * time.Second)
+		}
+	}
+}
+
// Then in printLogs function:
-	func() {
-		for {
-			select {
-			case <-ctx.Done():
-				return
-			default:
-				if success := fetcher(); !success {
-					return
-				}
-				if !tail {
-					return
-				}
-				time.Sleep(5 * time.Second)
-			}
-		}
-	}()
+	pollLogs(ctx, fetcher, tail)

179-181: Consider adding validation for severity levels.

The severity flag accepts any string value without validation. Consider validating against known severity levels to provide better user experience.

	if severity, _ := cmd.Flags().GetString("severity"); severity != "" {
+		validSeverities := []string{"trace", "debug", "info", "warn", "error", "fatal"}
+		if !slices.Contains(validSeverities, strings.ToLower(severity)) {
+			logger.Fatal("invalid severity level: %s. Valid options: %s", severity, strings.Join(validSeverities, ", "))
+		}
		query.Add("severity", severity)
	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6da9f0d and fd246c4.

📒 Files selected for processing (1)
  • cmd/logs.go (1 hunks)
🔇 Additional comments (5)
cmd/logs.go (5)

1-18: LGTM! Clean package structure and appropriate imports.

The package declaration and imports are well-organized and include all necessary dependencies for the CLI log functionality.


43-49: LGTM! Well-structured Log type.

The struct design is clean with appropriate JSON tags and reasonable field types for log data.


51-54: LGTM! Standard API response structure.

The LogsResponse struct follows a common pattern for API responses with success indication and data payload.


90-101: Review the deduplication logic for potential issues.

The current deduplication logic clears prevIds when none of the current response IDs are found in the previous set. This could cause issues in scenarios where the API returns non-overlapping result sets, potentially leading to missed or duplicate logs.

Consider a more robust approach:

-		// Check if any log in response.Data has an ID in prevIds
-		found := false
-		for _, log := range response.Data {
-			if slices.Contains(prevIds, log.ID) {
-				found = true
-				break
-			}
-		}
-		// If none of the IDs in the response are in prevIds, clear prevIds
-		if !found {
-			prevIds = prevIds[:0]
-		}
+		// Keep a reasonable limit on prevIds to prevent memory growth
+		if len(prevIds) > 1000 {
+			prevIds = prevIds[len(prevIds)-500:]
+		}

200-215: Comprehensive implementation covers all PR objectives.

The implementation successfully delivers all features mentioned in the PR objectives (hideDate, hideTime, tail, since) plus additional filtering capabilities for agents, deployments, environments, etc.

Comment thread cmd/logs.go Outdated
Comment thread cmd/logs.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cmd/logs.go (1)

76-101: Consider optimizing the deduplication logic for better performance.

The current deduplication approach using slices.Contains has O(n) complexity per log entry, which could become inefficient with large log volumes. Consider using a map for O(1) lookups.

-	prevIds := []string{}
+	prevIds := make(map[string]bool)
-		// Check if any log in response.Data has an ID in prevIds
-		found := false
-		for _, log := range response.Data {
-			if slices.Contains(prevIds, log.ID) {
-				found = true
-				break
-			}
-		}
-		// If none of the IDs in the response are in prevIds, clear prevIds
-		if !found {
-			prevIds = prevIds[:0]
-		}
+		// Check if any log in response.Data has an ID in prevIds
+		found := false
+		for _, log := range response.Data {
+			if prevIds[log.ID] {
+				found = true
+				break
+			}
+		}
+		// If none of the IDs in the response are in prevIds, clear prevIds
+		if !found {
+			prevIds = make(map[string]bool)
+		}
-		for _, log := range response.Data {
-			if slices.Contains(prevIds, log.ID) {
-				continue
-			}
-			prevIds = append(prevIds, log.ID)
+		for _, log := range response.Data {
+			if prevIds[log.ID] {
+				continue
+			}
+			prevIds[log.ID] = true
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd246c4 and 57ac106.

📒 Files selected for processing (1)
  • cmd/logs.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
cmd/logs.go (2)
Learnt from: pec1985
PR: agentuity/cli#342
File: cmd/logs.go:58-60
Timestamp: 2025-05-26T08:15:51.520Z
Learning: In the agentuity/cli codebase, util.GetURLs and util.EnsureLoggedIn functions don't return errors as their last return value. They handle errors internally and may call os.Exit(1) for fatal errors. The blank identifiers in the assignments are for other return values, not errors.
Learnt from: pec1985
PR: agentuity/cli#342
File: cmd/logs.go:58-60
Timestamp: 2025-05-26T08:15:51.520Z
Learning: In the agentuity/cli codebase, util.GetURLs returns (apiUrl, appUrl, transportUrl string) and util.EnsureLoggedIn returns (apikey, userId string). These functions handle errors internally and call os.Exit(1) for fatal conditions rather than returning errors. The blank identifiers in calls to these functions are for ignoring unused return values, not errors.
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (actions)
🔇 Additional comments (6)
cmd/logs.go (6)

21-41: LGTM! Well-designed flexible duration parser.

The implementation correctly handles both standard Go duration strings and the custom 'd' suffix for days. The regex pattern and parsing logic are sound, and the fallback to time.ParseDuration ensures compatibility with standard Go durations.


43-54: LGTM! Clean and appropriate data structures.

The Log and LogsResponse structs are well-defined with proper JSON tags and appropriate field types. The structure aligns well with the API response format and supports all the required log attributes.


67-74: LGTM! Comprehensive date/time formatting logic.

The conditional logic correctly handles all combinations of hideDate and hideTime flags, providing flexible display options for users. The use of Go's standard time format constants is appropriate.


124-139: LGTM! Excellent implementation of context-aware polling.

The polling loop correctly handles context cancellation and implements the tail functionality with appropriate sleep intervals. The immediate return on fetch failure is a good design choice.


183-191: LGTM! Robust duration parsing with appropriate error handling.

The integration of the custom duration parser with proper error handling and fallback to a default value ensures reliable functionality. The use of logger.Fatal for parsing errors is appropriate for a CLI tool.


200-215: LGTM! Comprehensive flag definitions with sensible defaults.

The flag setup covers all the filtering and display options mentioned in the PR objectives. The default values (especially env: "local" and since: "1h") are reasonable for typical usage patterns.

Comment thread cmd/logs.go Outdated
tail, _ := cmd.Flags().GetBool("tail")
hideDate, _ := cmd.Flags().GetBool("hideDate")
hideTime, _ := cmd.Flags().GetBool("hideTime")
printLogs(cmd.Context(), logger, cmd, query, tail, hideDate, hideTime)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a signal context here instead of a background ctx so that SIGINT etc works better:

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer cancel()

@jhaynie
Copy link
Copy Markdown
Member

jhaynie commented May 26, 2025

small nit but otherwise looks great. 💯

@pec1985 pec1985 requested a review from jhaynie May 26, 2025 15:58
Copy link
Copy Markdown
Contributor

@robindiddams robindiddams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@pec1985 pec1985 merged commit 5be3109 into main May 27, 2025
14 checks passed
@pec1985 pec1985 deleted the AGENT-122 branch May 27, 2025 08:58
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants